package lq

import (
	"sort"

	"gitee.com/hasika/gotool/container"
)

type ListQuery[T any] struct {
	rangeFunc func(v container.ListVisitor[T])
}

func NewListQuery[T any](items []T) ListQuery[T] {
	return ListQuery[T]{
		rangeFunc: func(v container.ListVisitor[T]) {
			for index := range items {
				item := items[index]
				if !v(item) {
					break
				}
			}
		},
	}
}

func NewListQueryFromIterable[T any](it container.ListIterable[T]) ListQuery[T] {
	return ListQuery[T]{
		rangeFunc: func(v container.ListVisitor[T]) {
			it.Iterate(v)
		},
	}
}

func NewListQueryFromMapKeys[T comparable, V any](items map[T]V) ListQuery[T] {
	return ListQuery[T]{
		rangeFunc: func(v container.ListVisitor[T]) {
			for k := range items {
				if !v(k) {
					break
				}
			}
		},
	}
}

func NewListQueryFromMapValues[K comparable, T any](items map[K]T) ListQuery[T] {
	return ListQuery[T]{
		rangeFunc: func(v container.ListVisitor[T]) {
			for _, item := range items {
				if !v(item) {
					break
				}
			}
		},
	}
}

func (q ListQuery[T]) Predict(acceptors ...container.ListAcceptor[T]) ListQuery[T] {
	return ListQuery[T]{
		rangeFunc: func(v container.ListVisitor[T]) {
			q.rangeFunc(func(t T) bool {
				accept := true
				for _, a := range acceptors {
					if !a(t) {
						accept = false
						break
					}
				}
				if accept {
					return v(t)
				}
				return true
			})
		},
	}
}

func (q ListQuery[T]) Iterate(v container.ListVisitor[T]) {
	q.rangeFunc(v)
}

func (q ListQuery[T]) First(v container.ListAcceptor[T]) T {
	var result T
	q.rangeFunc(func(t T) bool {
		accept := !v(t)
		if accept {
			result = t
			return false
		}
		return true
	})
	return result
}

func (q ListQuery[T]) All() []T {
	var result []T
	q.rangeFunc(func(item T) bool {
		result = append(result, item)
		return true
	})
	return result
}

func (q ListQuery[T]) Concat(other []T) ListQuery[T] {
	lq := NewListQuery(other)
	return ListQuery[T]{
		rangeFunc: func(v container.ListVisitor[T]) {
			shouldContinue := true
			q.rangeFunc(func(t T) (shouldContinue bool) {
				if !v(t) {
					shouldContinue = false
					return false
				}
				return true
			})
			if !shouldContinue {
				return
			}
			lq.Iterate(v)
		},
	}
}

func (q ListQuery[T]) Sort(compareFunc func(a T, b T) int) []T {
	result := q.All()
	sort.Slice(result, func(i, j int) bool {
		return compareFunc(result[i], result[j]) < 0
	})
	return result
}

func (q ListQuery[T]) Extend2(e IExtend[T]) IExtend[T] {
	e.init(&q)
	return e
}

type IExtend[T any] interface {
	init(q *ListQuery[T])
}

type Extend2[T1, T2 any] struct {
	q *ListQuery[T1]
}

func NewExtend2[T1, T2 any]() *Extend2[T1, T2] {
	return &Extend2[T1, T2]{}
}

func (e *Extend2[T1, T2]) init(q *ListQuery[T1]) {
	e.q = q
}

func (e *Extend2[T1, T2]) Select(f func(T1) T2) ListQuery[T2] {
	return ListQuery[T2]{
		rangeFunc: func(v container.ListVisitor[T2]) {
			e.q.rangeFunc(func(t T1) (shouldContinue bool) {
				return v(f(t))
			})
		},
	}
}
